# CellRL - A cellular roguelike. ###################################################################################

import termios, fcntl, sys, os

from microwidget import *

class Game:
  def __init__(self):
    self.map = [
           # 0123456789012345678901234567890123456789
       list("########################################"),
       list("#......................................#"),
       list("#...#####...#####...################...#"),
       list("#...#...#...#...#...#.@..@...#.....#...#"),
       list("#...#.@.#...#.@.#...#.@..@...#.....#...#"),
       list("#...#...#...#...#...+......@.+..>..#...#"),
       list("#...##+##...##+##...#.@..@...#.....#...#"),
       list("#...................#.@..@...#.....#...#"),
       list("#.....$.............################...#"),
       list("#......................................#"),
       list("#......................................#"),
       list("########################################")]
    # Find starting location of player - $
    tmpx = 0
    tmpy = 0
    done = False
    while tmpy < len(self.map) and done == False:
      tmpx = 0
      while tmpx < len(self.map[0]) and done == False:
        if self.map[tmpy][tmpx] == "$":
          self.pcx = tmpx
          self.pcy = tmpy
          self.map[tmpy][tmpx] = "."
          done = True
        tmpx += 1
      tmpy += 1
    
    # We need a Terminal Object
    self.term = Terminal()
    
    # In that terminal we need a
    # SubTerminal and just a base Widget
    # will suffice.
    self.logterm = SubTerminal(0,9,40,3)
    self.mapwidget = Widget(0,0,40,12-3)
    
    self.logterm.parent = self.term
    self.mapwidget.parent = self.term
    self.term.addchild(self.logterm)
    self.term.addchild(self.mapwidget)
    
    # Setup single character input mode
    self.fd = None
    self.attrs_save = None
    self.flags_save = None
    self.enablesci()
    
    self.done = False
  
  # This is so disablesci() is called
  # at exit
  def deinit(self):
    self.disablesci()
  
  def update(self):
    # Get input
    k = self.getch()
    
    if k == "w":
      # try moving north
      if self.checkdest(self.pcx,self.pcy-1):
        self.pcy -= 1
    elif k == "a":
      # try moving west
      if self.checkdest(self.pcx-1,self.pcy):
        self.pcx -= 1
    elif k == "s":
      # try moving south
      if self.checkdest(self.pcx,self.pcy+1):
        self.pcy += 1
    elif k == "d":
      # try moving east
      if self.checkdest(self.pcx+1,self.pcy):
        self.pcx += 1
    elif k == "Q":
      # quit
      self.done = True
  
  def checkdest(self, nx, ny):
    dst = None
    if nx < 0 or nx >= len(self.map[0]) or ny < 0 or ny >= len(self.map):
      dst = None
    else:
      dst = self.map[ny][nx]
    if dst == None:
      self.logterm.addstr("You cannot walk off the map.\n")
      return False
    elif dst == "#":
      self.logterm.addstr("You cannot walk through walls.\n")
      return False
    elif dst == "@":
      self.logterm.addstr("You cannot push cells out of the way.\n")
      return False
    elif dst == "+":
      self.logterm.addstr("You opened the door.\n")
      self.map[ny][nx] = "/"
      return False
    elif dst == "/":
      return True
    elif dst == ">":
      self.logterm.addstr("You cannot presently traverse the world on the z-axis.\n")
      return True
    elif dst == "<":
      self.logterm.addstr("You cannot presently traverse the world on the z-axis.\n")
      return True
    elif dst == ".":
      return True
    else:
      return False
  
  def draw(self):
    # First we need to figure out
    # which tiles of the map to draw.
    # The PC should always be in
    # the screen coordinate (20,5)
    minx = self.pcx - 20
    miny = self.pcy - 5
    
    # Now draw the visible map to
    # the Widget.
    self.mapwidget.clear()
    cx = minx
    cy = miny
    while cy < miny + 12-3:
      cx = minx
      while cx < minx + 40:
        ctile = " "
        if cx < 0 or cx >= len(self.map[0]) or cy < 0 or cy >= len(self.map):
          ctile = " "
        else:
          ctile = self.map[cy][cx]
        self.mapwidget.setch(
          cx-minx, cy-miny,
          ctile, CWHITE, CBLACK)
        cx += 1
      cy += 1
    
    # Draw PC
    self.mapwidget.setch(
      20, 5, "@", CBLUE, CBLACK)
    
    # Also make sure Terminal cursor
    # is in the bottom right so it
    # isn't distracting.
    self.term.setcsr(39,11)
    
    # Now do real drawing
    self.term.clear()
    self.term.drawall()
    # I wish I didn't have to do this
    self.term.drawall()

  def enablesci(self):
    self.fd = sys.stdin.fileno()
    # save old state
    self.flags_save = fcntl.fcntl(self.fd, fcntl.F_GETFL)
    self.attrs_save = termios.tcgetattr(self.fd)
    # make raw - the way to do this comes
    # from the termios(3) man page.
    attrs = list(self.attrs_save) # copy the stored version to update
    # iflag
    attrs[0] &= ~(termios.IGNBRK | termios.BRKINT | termios.PARMRK | termios.ISTRIP | termios.INLCR | termios.IGNCR | termios.ICRNL | termios.IXON)
    # oflag
    attrs[1] &= ~termios.OPOST
    # cflag
    attrs[2] &= ~(termios.CSIZE | termios.PARENB)
    attrs[2] |= termios.CS8
    # lflag
    attrs[3] &= ~(termios.ECHONL | termios.ECHO | termios.ICANON | termios.ISIG | termios.IEXTEN)
    termios.tcsetattr(self.fd, termios.TCSANOW, attrs)
    # turn off non-blocking
    fcntl.fcntl(self.fd, fcntl.F_SETFL, self.flags_save & ~os.O_NONBLOCK)

  def getch(self):
    # read a single keystroke
    ret = None
    try:
      ret = sys.stdin.read(1) # Returns a single character
    except KeyboardInterrupt:
      ret = 0
    return ret

  def disablesci(self):
    # restore old state
    termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.attrs_save)
    fcntl.fcntl(self.fd, fcntl.F_SETFL, self.flags_save)

# Start the actual game
g = Game()
g.draw()
while not g.done:
  g.update()
  g.draw()
g.deinit()
